package york.Game;

import java.util.Vector;

import york.Game.Unit.SmartUnit;
import york.Game.Unit.SmartUnitGroup;

/** AIManager
 * 
 * @author Interitus
 * 
 * @param AIGroupList
 * A vector of all the groups of units in game that are able to receieve and translate AI commands
 * 
 * @param AIList
 * A vector of all the units not in a group that are able to receive and translate AI commands
 */
public class AIManager {
	Vector<Object> AIList = new Vector<Object>();
	Vector<ChangeContainer> ChangeList = new Vector<ChangeContainer>();
	
	/** <b>update()</b>
	 * 
	 * @param dt <br>
	 * 
	 * This updates all the objects in the game that are able to receive commands<br>
	 * 
	 * If a unit is declared dead/inactive, it is removed from the AIManager<br>
	 * 
	 * If a unit is declared outside of a group, it is re-added to the list by itself.<br>
	 * 
	 * If a unit is within range of a group, it is added to the group and removed from the AIList<br>
	 */
	public void update(double dt) {
		Object ToUpdate;
		SmartUnit SM_Temp;
		SmartUnitGroup SMG_Temp;
		for(int i = AIList.size()-1;i >= 0;i--) {
			//Next object to update (either a SmartUnit or a SmartUnitGroup)
			ToUpdate = AIList.get(i);
			
			if(ToUpdate.getClass() == SmartUnit.class) {
				//A SmartUnit only needs to update
				SM_Temp = ((SmartUnit)ToUpdate);
				SM_Temp.UpdateAI(dt);
				//If the unit is inactive or dies, it is removed from the manager when it is safe
				if(SM_Temp.Dead()) {
					ChangeList.add(new ChangeContainer(ToUpdate,ChangeFlags.REMOVE));
				}
				else {
					//If the unit is not dead, it tries to join any nearby groups
					AddStrayToGroups(SM_Temp);
				}
			}
			else if(ToUpdate.getClass() == SmartUnitGroup.class) {
				SMG_Temp = ((SmartUnitGroup)ToUpdate);
				//If a group no longer has any members, it can be removed from the manager
				if(SMG_Temp.getMembers().size() == 0) {
					ChangeList.add(new ChangeContainer(SMG_Temp,ChangeFlags.REMOVE));
				}
				else {
					//If it does have members, it needs to update each one of them separately
					// instead calling the GroupUpdate on them
					for(int j = SMG_Temp.getMembers().size()-1;j >= 0;j--) {
						SM_Temp = SMG_Temp.getMember(i);
						SM_Temp.UpdateAI_Group(dt);
					}
					//If any member is declared outside of a group, it is added to a temporary Vector<ChangeContainer>
					// inside of GetStrays. All of these elements are added the AIManager list at once. They are removed
					// from the gorups list in GetStrays()
					ChangeList.addAll(SMG_Temp.GetStrays());
				}
			}
		}
		CleanupLists();
		
	}
	
	/** <b>CleanupLists()</b>
	 * <br>This function iterates through the ChangeList making changes as necessary.
	 * Although all it can do is add or remove objects to the main AIList.
	 */
	public void CleanupLists() {
		ChangeContainer ToChange;
		for(int i = ChangeList.size()-1;i >= 0;i--) {
			ToChange = ChangeList.get(i);
			switch(ToChange.Flag){
			case ADD:
				AIList.add(ToChange.MyObject);
				break;
			case REMOVE:
				AIList.remove(ToChange.MyObject);
				break;
			}
			
		}
		ChangeList.removeAllElements();
	}
	
	/** <b>AddStrayToGroups(SmartUnit a_Stray)</b> 
	 * 
	 * @param a_Stray <br>
	 * Determines if a SmartUnit is close enogh to any nearby groups to
	 *  be able to join them.<br>
	 * If it is, then it is added to that groups list and removed from the main AIList.<br>
	 * If it is not, it is left alone. 
	 */
	public void AddStrayToGroups(SmartUnit a_Stray) {
		Object CurGroup;
		for(int i = AIList.size()-1;i >= 0;i--) {
			CurGroup = AIList.get(i);
			//If the object is a group
			if(CurGroup.getClass() == SmartUnitGroup.class) {
				//Check if the Stray is within the groups influence range
				// * - Stray  @ - Group Center (,) - Group Borders
				// * ( @ ) - Outside of influence range
				// ( * @   ) - Inside of influence range
				// Distance of ( to @ is determined by the Groups size
				if(a_Stray.getPosition().distance(((SmartUnitGroup)CurGroup).getPosition()) <= ((SmartUnitGroup)CurGroup).getSize()) {
					//If it is within the influence range, it is added to the group and removed from the AIManager list when its safe
					if(((SmartUnitGroup)CurGroup).AddMember(a_Stray)) {
						//If the member was not properly added, this section is skipped because it still needs to be able to update
						ChangeList.add(new ChangeContainer(a_Stray,ChangeFlags.REMOVE));
					}
				}
			}
		}
	}
	
	/** <b>AddUnit(Object a_ToAdd)</b>
	 * 
	 * @param a_ToAdd
	 * @return A boolean value is returned. This value is true if it was added, and false if it was not. <br>
	 * If the Object being added was a SmartUnit, it attempts to add it to a group. The group add would override the first
	 *  attempt at adding, by removing it from the main list and adding it to a groups list.
	 */
	public boolean AddUnit(Object a_ToAdd) {
		if(a_ToAdd == null)
			return false;
		AIList.add(a_ToAdd);
		if(a_ToAdd.getClass() == SmartUnit.class) {
			AddStrayToGroups(((SmartUnit)a_ToAdd));
		}
		return true;
	}
	
	/** <b>RemoveUnit(Object a_ToRemove)</b>
	 * 
	 * @param a_ToRemove
	 * @return A boolean value. This value is true if the Object was removed and false if it was not. <br>
	 * It first checks if the main list contains this specific object. If it does it is removed. <br>A SmartUnit
	 *  within a groups list does not need to be removed with this function. The main AIList looks at stray SmartUnits
	 *  and SmartUnitGroups. It doesn't matter if a groups local list loses a unit, if it drops to zero, the group will be removed
	 *  at the next update.<br>This function is to be used on stray SmartUnits, and SmartUnitGroups.
	 */
	public boolean RemoveUnit(Object a_ToRemove) {
		if(a_ToRemove == null)
			return false;
		if(AIList.contains(a_ToRemove)) {
			AIList.remove(a_ToRemove);
			return true;
		}
		return false;
	}

}
